package com.swak.rpc.config.boot;

import static java.util.concurrent.Executors.newSingleThreadExecutor;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.swak.reactivex.threads.SwakThreadFactory;
import com.swak.rpc.config.ServiceConfig;
import com.swak.rpc.config.context.ConfigManager;
import com.swak.rpc.config.model.ApplicationModel;

/**
 * 启动器
 * 
 * @author lifeng
 */
@SuppressWarnings("rawtypes")
public class Bootstrap {

	private static volatile Bootstrap INSTANCE;

	public static Bootstrap getInstance() {
		if (INSTANCE == null) {
			synchronized (Bootstrap.class) {
				if (INSTANCE == null) {
					INSTANCE = new Bootstrap();
				}
			}
		}
		return INSTANCE;
	}

	private final ExecutorService executorService = newSingleThreadExecutor(
			new SwakThreadFactory("Rpc-Awaited", false));
	private final Logger logger = LoggerFactory.getLogger(Bootstrap.class);
	private final AtomicBoolean initialized = new AtomicBoolean(false);
	private final AtomicBoolean started = new AtomicBoolean(false);
	private final AtomicBoolean ready = new AtomicBoolean(false);
	private final AtomicBoolean destroyed = new AtomicBoolean(false);
	private final AtomicBoolean awaited = new AtomicBoolean(false);
	private final Lock destroyLock = new ReentrantLock();
	private final Map<String, ServiceConfig<?>> exportedServices = new ConcurrentHashMap<>();
	private final Lock awaitLock = new ReentrantLock();
	private final Condition awaitCondition = awaitLock.newCondition();
	private ConfigManager configManager;

	private Bootstrap() {
		configManager = ApplicationModel.getConfigManager();
		ShutdownHook.getInstance().register();
		ShutdownHook.getInstance().addCallback(Bootstrap.this::destroy);
	}

	/**
	 * 启动服务
	 * 
	 * @return
	 */
	public Bootstrap start() {
		if (started.compareAndSet(false, true)) {
			destroyed.set(false);
			ready.set(false);
			initialize();
			if (logger.isInfoEnabled()) {
				logger.info("RPC is starting...");
			}

			// 1. export Rpc Services
			exportServices();

			// 2. export Rpc Services
			referServices();

			ready.set(true);
			if (logger.isInfoEnabled()) {
				logger.info("RPC is ready.");
			}
			if (logger.isInfoEnabled()) {
				logger.info("RPC has started.");
			}

		}
		return this;
	}

	private void initialize() {
		if (!initialized.compareAndSet(false, true)) {
			return;
		}
	}

	private void exportServices() {
		configManager.getServices().forEach(sc -> {
			ServiceConfig serviceConfig = (ServiceConfig) sc;
			exportService(serviceConfig);
		});
	}

	private void exportService(ServiceConfig sc) {
		if (exportedServices.containsKey(sc.getServiceName())) {
			throw new IllegalStateException("There are multiple ServiceBean instances with the same service name: ["
					+ sc.getServiceName() + "], instances: [" + exportedServices.get(sc.getServiceName()).toString()
					+ ", " + sc.toString()
					+ "]. Only one service can be exported for the same triple (group, interface, version), "
					+ "please modify the group or version if you really need to export multiple services of the same interface.");
		}
		sc.export();
		exportedServices.put(sc.getServiceName(), sc);
	}

	private void referServices() {

	}

	public void destroy() {
		if (destroyLock.tryLock()) {
			try {
				if (destroyed.compareAndSet(false, true)) {
					if (started.compareAndSet(true, false)) {
						unexportServices();
						unreferServices();
					}
				}

			} finally {
				initialized.set(false);
				destroyLock.unlock();
			}
		}
	}

	private void unexportServices() {
		exportedServices.forEach((serviceName, sc) -> {
			configManager.removeConfig(sc);
			sc.unexport();
		});
		exportedServices.clear();
	}

	private void unreferServices() {

	}

	/**
	 * 等待 Rpc 停止服务
	 * 
	 * @return
	 */
	public Bootstrap await() {
		if (!awaited.get()) {
			if (!executorService.isShutdown()) {
				executeMutually(() -> {
					while (!awaited.get()) {
						if (logger.isInfoEnabled()) {
							logger.info("Rpc awaiting ...");
						}
						try {
							awaitCondition.await();
						} catch (InterruptedException e) {
							Thread.currentThread().interrupt();
						}
					}
				});
			}
		}
		return this;
	}

	private void executeMutually(Runnable runnable) {
		awaitLock.lock();
		try {
			runnable.run();
		} finally {
			awaitLock.unlock();
		}
	}

	/**
	 * 停止 Rpc 服务
	 * 
	 * @return
	 * @throws IllegalStateException
	 */
	public Bootstrap stop() throws IllegalStateException {
		destroy();
		return this;
	}
}